<route lang="json5">
{
  style: {
    navigationBarTitleText: '时间选择器',
  },
}
</route>
<template>
  <view class="time-slider-container">
    <!-- 选中时间显示 -->
    <view class="selected-time-display" v-if="selectedStartTime">
      <text>{{ formatTime(selectedStartTime) }} - {{ formatTime(getSelectedEndTime()) }}</text>
    </view>

    <!-- 时间选择器主体 -->
    <view class="slider-wrapper">
      <view
        id="time-slider"
        ref="sliderRef"
        class="slider-track"
        @touchstart="handleTouchStart"
        @touchmove="handleTouchMove"
        @touchend="handleTouchEnd"
      >
        <!-- 背景时间段 -->
        <view
          v-for="(segment, index) in timeSegments"
          :key="index"
          class="time-segment"
          :class="{
            disabled: !segment.available,
          }"
          :style="{ width: `${segment.width}%` }"
        ></view>

        <!-- 选中时间段 -->
        <view
          v-if="selectedStartTime"
          class="selected-segment"
          :style="{
            left: `${getSelectedStartPosition()}%`,
            width: `${getSelectedWidth()}%`,
          }"
        ></view>
      </view>

      <!-- 时间点标记 -->
      <view class="time-marks">
        <view
          v-for="(mark, index) in timeMarks"
          :key="index"
          class="time-mark"
          :style="{ left: `${mark.position}%` }"
        >
          <text class="mark-text">{{ mark.time }}</text>
        </view>
      </view>
    </view>

    <!-- 总时间范围显示 -->
    <view class="time-range-display">
      <text>{{ formatTime(startTime) }}</text>
      <text>{{ formatTime(endTime) }}</text>
    </view>
  </view>
</template>

<script lang="ts" setup>
import { ref, computed } from 'vue'

// 时间配置
const startTime = ref(new Date(2024, 0, 1, 8, 0)) // 开始时间 8:00
const endTime = ref(new Date(2024, 0, 1, 20, 0)) // 结束时间 20:00
const selectedDuration = ref(60) // 选中时间段长度（分钟）
const selectedStartTime = ref<Date | null>(null)
const isDragging = ref(false)
const sliderRef = ref<HTMLElement | null>(null)

// 不可选时间段
const unavailableSegments = [
  { start: new Date(2024, 0, 1, 12, 0), end: new Date(2024, 0, 1, 13, 0) }, // 12:00-13:00
  { start: new Date(2024, 0, 1, 17, 0), end: new Date(2024, 0, 1, 18, 0) }, // 17:00-18:00
]

// 计算时间点标记
const timeMarks = computed(() => {
  const marks = []
  const totalMinutes = (endTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)
  const interval = 120 // 每2小时显示一个时间点

  for (let i = 0; i <= totalMinutes; i += interval) {
    const time = new Date(startTime.value.getTime() + i * 60 * 1000)
    marks.push({
      time: formatTime(time),
      position: (i / totalMinutes) * 100,
    })
  }

  return marks
})

// 计算时间段的显示
const timeSegments = computed(() => {
  const totalMinutes = (endTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)
  const segmentDuration = 30 // 每个小段30分钟
  const segments = []

  for (let i = 0; i < totalMinutes; i += segmentDuration) {
    const segmentStart = new Date(startTime.value.getTime() + i * 60 * 1000)
    const segmentEnd = new Date(segmentStart.getTime() + segmentDuration * 60 * 1000)

    const isAvailable = !unavailableSegments.some(
      (segment) =>
        (segmentStart >= segment.start && segmentStart < segment.end) ||
        (segmentEnd > segment.start && segmentEnd <= segment.end),
    )

    segments.push({
      start: segmentStart,
      end: segmentEnd,
      available: isAvailable,
      width: (segmentDuration / totalMinutes) * 100,
    })
  }

  return segments
})

// 格式化时间显示
const formatTime = (date: Date) => {
  return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
}

// 获取选中时间段的结束时间
const getSelectedEndTime = () => {
  if (!selectedStartTime.value) return null
  return new Date(selectedStartTime.value.getTime() + selectedDuration.value * 60 * 1000)
}

// 获取选中时间段的起始位置百分比
const getSelectedStartPosition = () => {
  if (!selectedStartTime.value) return 0
  const totalMinutes = (endTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)
  const startMinutes = (selectedStartTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)
  return (startMinutes / totalMinutes) * 100
}

// 获取选中时间段的宽度百分比
const getSelectedWidth = () => {
  const totalMinutes = (endTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)
  return (selectedDuration.value / totalMinutes) * 100
}

// 获取元素位置信息
const getElementRect = () => {
  return new Promise<UniApp.NodeInfo>((resolve) => {
    const query = uni.createSelectorQuery()
    query
      .select('#time-slider')
      .boundingClientRect((data) => {
        resolve(data)
      })
      .exec()
  })
}

// 获取当前空白区块的起始和结束时间
const getCurrentAvailableSegment = (time: Date) => {
  const totalMinutes = (endTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)
  const segmentDuration = 30 // 每个小段30分钟

  // 找到包含当前时间的空白区块
  for (let i = 0; i < totalMinutes; i += segmentDuration) {
    const segmentStart = new Date(startTime.value.getTime() + i * 60 * 1000)
    const segmentEnd = new Date(segmentStart.getTime() + segmentDuration * 60 * 1000)

    const isAvailable = !unavailableSegments.some(
      (unavailable) =>
        (segmentStart >= unavailable.start && segmentStart < unavailable.end) ||
        (segmentEnd > unavailable.start && segmentEnd <= unavailable.end),
    )

    if (isAvailable && time >= segmentStart && time < segmentEnd) {
      return {
        start: segmentStart,
        end: segmentEnd,
      }
    }
  }

  return null
}

// 检查时间点是否在不可选时间段内
const isTimeInUnavailableSegment = (time: Date) => {
  const potentialEnd = new Date(time.getTime() + selectedDuration.value * 60 * 1000)
  return unavailableSegments.some(
    (unavailable) =>
      (time >= unavailable.start && time < unavailable.end) ||
      (potentialEnd > unavailable.start && potentialEnd <= unavailable.end),
  )
}

// 处理触摸开始
const handleTouchStart = async (event: any) => {
  try {
    const rect = await getElementRect()
    if (!rect) return

    const touch = event.touches[0]
    const touchX = touch.clientX

    // 计算触摸点在滑块上的相对位置
    const relativeX = touchX - rect.left
    const percentage = relativeX / rect.width

    // 计算总时间范围（分钟）
    const totalMinutes = (endTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)

    // 计算新的开始时间
    const minutesFromStart = Math.floor(percentage * totalMinutes)
    const newStartTime = new Date(startTime.value.getTime() + minutesFromStart * 60 * 1000)

    // 检查是否在不可选时间段内
    if (isTimeInUnavailableSegment(newStartTime)) {
      return
    }

    const currentSegment = getCurrentAvailableSegment(newStartTime)
    if (currentSegment) {
      isDragging.value = true
      selectedStartTime.value = newStartTime
    }
  } catch (error) {
    console.error('获取元素位置失败:', error)
  }
}

// 处理触摸移动
const handleTouchMove = async (event: any) => {
  if (!isDragging.value || !selectedStartTime.value) return

  try {
    const rect = await getElementRect()
    if (!rect) return

    const touch = event.touches[0]
    const touchX = touch.clientX

    // 计算触摸点在滑块上的相对位置
    const relativeX = touchX - rect.left
    const percentage = relativeX / rect.width

    // 计算总时间范围（分钟）
    const totalMinutes = (endTime.value.getTime() - startTime.value.getTime()) / (1000 * 60)

    // 计算新的开始时间
    const minutesFromStart = Math.floor(percentage * totalMinutes)
    const newStartTime = new Date(startTime.value.getTime() + minutesFromStart * 60 * 1000)

    // 获取当前空白区块
    const currentSegment = getCurrentAvailableSegment(selectedStartTime.value)
    if (!currentSegment) return

    // 计算选中时间段的结束时间
    const potentialEnd = new Date(newStartTime.getTime() + selectedDuration.value * 60 * 1000)

    // 限制在空白区块内移动
    let finalStartTime = newStartTime

    // 如果开始时间小于空白区块开始时间，则限制在开始时间
    if (newStartTime < currentSegment.start) {
      finalStartTime = currentSegment.start
    }

    // 如果结束时间大于空白区块结束时间，则限制在结束时间减去选中时长
    if (potentialEnd > currentSegment.end) {
      finalStartTime = new Date(currentSegment.end.getTime() - selectedDuration.value * 60 * 1000)
    }

    // 检查是否在不可选时间段内
    if (!isTimeInUnavailableSegment(finalStartTime)) {
      selectedStartTime.value = finalStartTime
    }
  } catch (error) {
    console.error('获取元素位置失败:', error)
  }
}

// 处理触摸结束
const handleTouchEnd = () => {
  isDragging.value = false
}
</script>

<style lang="scss" scoped>
.time-slider-container {
  width: 100%;
  padding: 20rpx;
  box-sizing: border-box;
}

.selected-time-display {
  text-align: center;
  font-size: 32rpx;
  color: #1890ff;
  margin-bottom: 20rpx;
  font-weight: bold;
}

.slider-wrapper {
  width: 80%;
  margin: 0 auto;
  position: relative;
}

.slider-track {
  height: 40rpx;
  background-color: #f5f5f5;
  border-radius: 20rpx;
  display: flex;
  overflow: hidden;
  border: 2rpx solid #d9d9d9;
  touch-action: none;
  position: relative;
}

.time-segment {
  height: 100%;
  transition: background-color 0.3s;

  &.disabled {
    background-color: #d9d9d9;
  }

  &:not(.disabled) {
    background-color: #ffffff;
  }
}

.selected-segment {
  position: absolute;
  height: 100%;
  background-color: #1890ff;
  transition: all 0.3s;
  pointer-events: none;
}

.time-marks {
  position: relative;
  height: 40rpx;
  margin-top: 10rpx;
}

.time-mark {
  position: absolute;
  transform: translateX(-50%);

  &::before {
    content: '';
    position: absolute;
    left: 50%;
    top: -10rpx;
    width: 2rpx;
    height: 10rpx;
    background-color: #999;
  }
}

.mark-text {
  font-size: 24rpx;
  color: #666;
  white-space: nowrap;
  transform: translateX(-50%);
  display: block;
  text-align: center;
}

.time-range-display {
  display: flex;
  justify-content: space-between;
  margin-top: 20rpx;
  font-size: 28rpx;
  color: #333;
}
</style>
